package org.senseframework.support.orm.hbase.utils;

import org.senseframework.support.orm.hbase.annotation.HBaseColumn;
import org.senseframework.support.orm.hbase.annotation.HBaseTable;
import org.senseframework.support.orm.hbase.annotation.RowKey;
import org.senseframework.support.orm.hbase.datasource.HBaseDataSource;
import org.senseframework.support.orm.hbase.definition.HBaseColumnDefinition;
import org.senseframework.support.orm.hbase.definition.HBaseEntityDefinition;
import org.senseframework.support.orm.hbase.query.HBase;
import org.senseframework.support.orm.hbase.serializer.ISerialize;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wangqian
 * @date 18/4/14 上午10:03
 */
public class HBaseEntityUtils {

    private static final Logger logger = LoggerFactory.getLogger(HBase.class);

    private final static Map<Class, HBaseEntityDefinition> entityDefinitions = new ConcurrentHashMap<Class, HBaseEntityDefinition>();

    public static void initial(Class clazz) {
        if (!entityDefinitions.containsKey(clazz)) {
            synchronized (HBaseEntityUtils.class) {
                if (!entityDefinitions.containsKey(clazz)) {
                    entityDefinitions.put(clazz, resolve(clazz));
                }
            }
        }
    }

    private final static HBaseEntityDefinition table(Class clazz) {
        if (clazz.isAnnotationPresent(HBaseTable.class)) {
            HBaseEntityDefinition entityDefinition = new HBaseEntityDefinition();
            HBaseTable table = (HBaseTable) clazz.getAnnotation(HBaseTable.class);
            entityDefinition.setTable(table.value());
            //todo table自定义序列化方式
            if (table.serialize() != null && table.serialize().length() > 0) {
                try {
                    entityDefinition.setSerialize((ISerialize) Class.forName(table.serialize()).newInstance());
                } catch (Exception e) {
                    logger.error(table.serialize() + " not support ,error detail:{}", e);
                    throw new RuntimeException(table.serialize() + " serializer not support !");
                }
            } else {
                entityDefinition.setSerialize(HBaseDataSource.getDefaultSerialize());
            }
            return entityDefinition;
        }
        throw new RuntimeException("the entity not use @HBaseTable, Please check it.");
    }

    private final static void registerColumns(List<Field> fields, HBaseEntityDefinition entityDefinition) {
        for (Field field : fields) {
            if (field.isAnnotationPresent(HBaseColumn.class)) {
                HBaseColumn column = field.getAnnotation(HBaseColumn.class);
                HBaseColumnDefinition columnDefinition = new HBaseColumnDefinition();
                columnDefinition.setFamily(column.family());
                columnDefinition.setQualifier(column.qualifier());
                columnDefinition.setJavaColumn(field.getName());
                columnDefinition.setType(field.getType());
                columnDefinition.setKeyType(column.keyType());
                columnDefinition.setValueType(column.valueType());
                columnDefinition.setField(field);
                columnDefinition.setColumnType(column.type());
                entityDefinition.addColumnDefinition(field.getName(), columnDefinition);
            } else if (field.isAnnotationPresent(RowKey.class)) {
                entityDefinition.setIdJavaColumn(field.getName());
                entityDefinition.setIdType(field.getType());
            }
        }
    }

    protected final static List<Field> allFields(Class cls) {
        List<Field> all = new ArrayList<Field>();
        all.addAll(Arrays.asList(cls.getDeclaredFields()));
        return all;
    }

    public final static HBaseEntityDefinition resolve(Class clazz) {
        HBaseEntityDefinition entityDefinition = table(clazz);
        registerColumns(allFields(clazz), entityDefinition);
        return entityDefinition;
    }

    public static HBaseEntityDefinition findEntityDefinition(Class clazz) {
        return findEntityDefinition(clazz, false);
    }

    public static HBaseEntityDefinition findEntityDefinition(Class clazz, boolean autoCreate) {
        HBaseEntityDefinition entityDefinition = entityDefinitions.get(clazz);
        if (entityDefinition == null) {
            entityDefinition = resolve(clazz);
            entityDefinitions.put(clazz, entityDefinition);
            if (!autoCreate) {
                logger.warn("table " + entityDefinition.getTable() + " may not create, please check annotationPackages in xml.");
            }
        }
        return entityDefinition;
    }
}
